﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

using Linyb.Linq.QueryExtension.Mapper;

namespace Linyb.Linq.QueryExtension
{
    public static class Predicates
    {
        /// <summary>
        /// Factory method that creates a new IFieldPredicate predicate: [FieldName] [Operator] [Value]. 
        /// Example: WHERE FirstName = 'Foo'
        /// </summary>
        /// <typeparam name="T">The type of the entity.</typeparam>
        /// <param name="expression">An expression that returns the left operand [FieldName].</param>
        /// <param name="op">The comparison operator.</param>
        /// <param name="value">The value for the predicate.</param>
        /// <param name="not">Effectively inverts the comparison operator. Example: WHERE FirstName &lt;&gt; 'Foo'.</param>
        /// <returns>An instance of IFieldPredicate.</returns>
        public static IFieldPredicate Field<T>(Expression<Func<T, object>> expression, Operator op, object value, bool not = false) where T : class
        {
            PropertyInfo propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
            return new FieldPredicate<T>
            {
                PropertyName = propertyInfo.Name,
                Operator = op,
                Value = value,
                Not = not
            };
        }

        public static IFieldPredicate Field<T>(PredicateData pd) where T : class
        {
            ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
            var expression = Expression.Lambda<Func<T, object>>(
                Expression.Convert(Expression.Property(parameter, pd.ColumnItem), typeof(object)),
                parameter);

            PropertyInfo propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
            return new FieldPredicate<T>
            {
                PropertyName = propertyInfo.Name,
                Operator = pd.GetOperator(),
                Value = pd.Value,
                Not = pd.Not
            };
        }

        /// <summary>
        /// 采用and的组合条件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pd"></param>
        /// <returns></returns>
        public static IPredicateGroup FieldsAnd<T>(IList<PredicateData> pds) where T : class
        {
            PredicateGroup group = new PredicateGroup { Operator = GroupOperator.And, Predicates = new List<IPredicate>() };

            foreach (var pd in pds)
            {
                ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
                var expression = Expression.Lambda<Func<T, object>>(
                    Expression.Convert(Expression.Property(parameter, pd.ColumnItem), typeof(object)),
                    parameter);

                PropertyInfo propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
                group.Predicates.Add(new FieldPredicate<T>
                {
                    PropertyName = propertyInfo.Name,
                    Operator = pd.GetOperator(),
                    Value = pd.Value,
                    Not = pd.Not
                });
            }

            return group;
        }

        /// <summary>
        /// Factory method that creates a new IPredicateGroup predicate.
        /// Predicate groups can be joined together with other predicate groups.
        /// </summary>
        /// <param name="op">The grouping operator to use when joining the predicates (AND / OR).</param>
        /// <param name="predicate">A list of predicates to group.</param>
        /// <returns>An instance of IPredicateGroup.</returns>
        public static IPredicateGroup Group(GroupOperator op, params IPredicate[] predicate)
        {
            return new PredicateGroup
            {
                Operator = op,
                Predicates = predicate
            };
        }

        /// <summary>
        /// Factory method that creates a new IBetweenPredicate predicate. 
        /// </summary>
        public static IBetweenPredicate Between<T>(Expression<Func<T, object>> expression, BetweenValues values)
            where T : class
        {
            PropertyInfo propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
            return new BetweenPredicate<T>
            {
                PropertyName = propertyInfo.Name,
                Value = values
            };
        }
    }
}